package dblink

import (
	"context"
	"errors"
	"fmt"
	"time"

	"gitee.com/night-tc/gobige/global"
	"gitee.com/night-tc/gobige/logger"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	gormlogger "gorm.io/gorm/logger"
	"gorm.io/gorm/utils"
)

// DBoption MysqlAccess的设置器
type DBoption func(md *MysqlAccess)

// MysqlAccess Mysql连接科器
type MysqlAccess struct {
	gorm.DB
	Config global.MysqlConfig
}

// SetMysqlAccessByConfig 设置数据库的连接字符串
func SetMysqlAccessByConfig(cfg *global.MysqlConfig) DBoption {
	return func(md *MysqlAccess) {
		md.Config = *cfg
	}
}

// NewMysqlAccess 生成一个连接器
func NewMysqlAccess(opts ...DBoption) *MysqlAccess {
	result := new(MysqlAccess)
	result.Config = global.MysqlConfig{
		MySQLUser:         "root",
		MySQLPwd:          "123456",
		MySQLAddr:         "127.0.0.1:3306",
		MySQLDB:           "gamedata",
		MySQLMaxIdleConns: 0,
		MySQLMaxOpenConns: 0,
		MySQLMaxLifetime:  0,
	}

	for i := range opts {
		opts[i](result)
	}
	if db, err := gorm.Open(mysql.Open(result.Config.ToDataSource()), &GormConfig{SlowThreshold: 200 * time.Millisecond}); err != nil {
		logger.Error("NewMysqlAccess ", err)

	} else {
		result.DB = *db
		sqlDB, _ := db.DB()

		// SetMaxIdleConns 设置空闲连接池中连接的最大数量
		sqlDB.SetMaxIdleConns(result.Config.MySQLMaxIdleConns)

		// SetMaxOpenConns 设置打开数据库连接的最大数量。
		sqlDB.SetMaxOpenConns(result.Config.MySQLMaxOpenConns)

		// SetConnMaxLifetime 设置了连接可复用的最大时间。
		sqlDB.SetConnMaxLifetime(result.Config.MySQLMaxLifetime)
	}
	if tx := result.Exec("select now()"); tx.Error != nil {
		logger.Error(tx.Error)
	} else {
		logger.Info("Mysql Access init.")
	}

	return result
}

type GormConfig struct {
	//慢查询时间
	SlowThreshold time.Duration
}

func (cfg *GormConfig) Apply(v *gorm.Config) error {
	v.Logger = cfg
	return nil
}
func (cfg *GormConfig) AfterInitialize(v *gorm.DB) error {
	return nil
}

func (cfg *GormConfig) LogMode(lv gormlogger.LogLevel) gormlogger.Interface {
	return cfg
}

func (cfg *GormConfig) Info(ctx context.Context, msg string, data ...interface{}) {
	logger.Infof(msg, data...)
}

func (cfg *GormConfig) Warn(ctx context.Context, msg string, data ...interface{}) {
	logger.Warnf(msg, data...)
}

func (cfg *GormConfig) Error(ctx context.Context, msg string, data ...interface{}) {
	logger.Errorf(msg, data...)
}

func (l *GormConfig) Trace(ctx context.Context, begin time.Time, fc func() (sql string, rowsAffected int64), err error) {

	elapsed := time.Since(begin)
	switch {
	case err != nil && !errors.Is(err, gormlogger.ErrRecordNotFound):
		sql, rows := fc()
		if rows == -1 {
			logger.Error(utils.FileWithLineNum(), err, elapsed.Milliseconds(), "-", sql)
		} else {
			// 正常会使用DB的索引约束进行操作与查询，外面会捕获错误输出日志
			// logger.Error(utils.FileWithLineNum(), err, elapsed.Milliseconds(), rows, sql)
		}
	case elapsed > l.SlowThreshold && l.SlowThreshold != 0:
		sql, rows := fc()
		slowLog := fmt.Sprintf("SLOW SQL >= %v", l.SlowThreshold)
		if rows == -1 {
			logger.Warn(utils.FileWithLineNum(), slowLog, elapsed.Milliseconds(), "-", sql)
		} else {
			logger.Warn(utils.FileWithLineNum(), slowLog, elapsed.Milliseconds(), rows, sql)
		}
	default:
		sql, rows := fc()
		if rows == -1 {
			logger.Debug(logger.LogKey_Mysql, elapsed.Milliseconds(), "-", sql)
		} else {
			logger.Debug(logger.LogKey_Mysql, elapsed.Milliseconds(), rows, sql)
		}
	}
}
